# Filters added to this controller apply to all controllers in the application.
# Likewise, all the methods added will be available for all controllers.

class ApplicationController < ActionController::Base
  # Pick a unique cookie name to distinguish our session data from others'
  session :session_key => '_contenid-os_session_id'

  # Sistema de autenticación.
  include AuthenticatedSystem


  # If you want "remember me" functionality, add this before_filter to Application Controller
  before_filter :login_from_cookie

  # Pone las variables para lo que se necesite a los lados.
  def prepare_layout
    if self.logged_in?
      now = Time.now
      if self.is_admin?
        # Ultimos instructores en hacer log in.
        @side_instructors = User.find(:all, :conditions => ['kind = ? AND last_login IS NOT NULL AND activation_code IS NULL', 'Instructor'], :order => 'last_login DESC', :limit => 15)
        # Ultimos administradores en hacer log in.
        @side_admins = User.find(:all, :conditions => ['kind = ? AND last_login IS NOT NULL AND activation_code IS NULL AND NOT login = ?', 'Admin', self.current_user.login], :order => 'last_login DESC', :limit => 15)
        # Ultimos estudiantes en hacer log in.
        @side_students = User.find(:all, :conditions => ['kind = ? AND last_login IS NOT NULL AND activation_code IS NULL', 'Student'], :order => 'last_login DESC', :limit => 15)
        # Lista de cursos
        @side_courses = Course.find(:all, :conditions => ['end_date > NOW() OR end_date IS NULL'], :order => 'end_date DESC', :limit => 15)
        # Sólo eventos globales aparecen en este calendario.
        @side_events_days = Course.find_by_name('Eventos Globales').events.map { |e| e.due_date.to_date }.select { |d| d.month == (params[:month] || now.month) }

      else
        # Eventos personales: Eventos creados por uno mismo.
        @side_events_own = next_n_events(self.current_user.events,15)
        # Eventos de curso: Eventos relacionados con los cursos de uno
        @side_events_courses = next_n_events(self.current_user.courses.map { |c| c.events }.flatten,15)

        # Eventos del calendario.
        @side_events_days = (Course.find_by_name('Eventos Globales').events +  self.current_user.courses.map { |c| [c.events, c.assignments] }.flatten ).map { |e| e.due_date.to_date }.select { |d| d.month == (params[:month] || now.month) }
        # Eventos globales: Eventos relacionados con el curso con id = 1.
        @side_events_global = next_n_events(Course.find_by_name('Eventos Globales').events, 15)
        if self.is_student?
          @side_courses = self.current_user.courses
        else
          @side_courses = self.current_user.created_courses + self.current_user.courses
        end
      end
    end
  end

  before_filter :prepare_layout

  # Regresa los siguientes N eventos (a partir de hoy) de una lista de eventos.
  def next_n_events(evnt_array,n)
    t = Time.now
    r = evnt_array.sort { |a,b| a.expire_date <=> b.expire_date }
    indx = binary_search(r) { |val|
      val.expire_date <=> t
    }
    if indx == -1
      return nil
    end
    r[indx,n]
  end

  # Esta es una busqueda binaria que regresa el indice en el que se encuentra un
  # dato en una lista ordenada. Si el dato no está, regresa el indice en el que
  # se debería encontrar (es decir array[indice-1] < dato <= array[indice+1]).
  def binary_search(array)
    inf = 0
    sup = array.size-1
    med = sup/2
    while inf < sup
      r = yield array[med]
      if r == 0
        while med > 0 and (yield array[med-1]) == 0
          med -= 1
        end
        return med
      end

      ri = yield array[inf]
      if ri == 0
        return inf
      end

      rs = yield array[sup]
      if rs == 0
        return sup
      elsif rs < 0
        return -1
      end

      if r < 0
        sup = med-1
      else
        inf = med+1
      end
      med = (inf+sup)/2
    end
    # Regresamos el punto en que debería de estar.
    while med > 0 and (yield array[med-1]) >= 0
      med -= 1
    end
    med
  end

end
